"use strict";
var util = require("util");
var fs = require("fs");
var path = require("path");

var helper = require("./helper.js");
var logger = helper.getLogger("update-anchor-peers");

var updateAnchorPeers = async function(
  channelName,
  configUpdatePath,
  username,
  org_name
) {
  logger.debug(
    "\n====== Updating Anchor Peers on '" + channelName + "' ======\n"
  );
  var error_message = null;
  try {
    // first setup the client for this org
    var client = await helper.getClientForOrg(org_name, username);
    logger.debug(
      'Successfully got the fabric client for the organization "%s"',
      org_name
    );
    var channel = client.getChannel(channelName);
    if (!channel) {
      let message = util.format(
        "Channel %s was not defined in the connection profile",
        channelName
      );
      logger.error(message);
      throw new Error(message);
    }

    // read in the envelope for the channel config raw bytes
    var envelope = fs.readFileSync(path.join(__dirname, configUpdatePath));
    // extract the channel config bytes from the envelope to be signed
    var channelConfig = client.extractChannelConfig(envelope);

    //Acting as a client in the given organization provided with "orgName" param
    // sign the channel config bytes as "endorsement", this is required by
    // the orderer's channel creation policy
    // this will use the admin identity assigned to the client when the connection profile was loaded
    let signature = client.signChannelConfig(channelConfig);

    let request = {
      config: channelConfig,
      signatures: [signature],
      name: channelName,
      txId: client.newTransactionID(true) // get an admin based transactionID
    };

    var promises = [];
    let event_hubs = channel.getChannelEventHubsForOrg();
    logger.debug(
      "found %s eventhubs for this organization %s",
      event_hubs.length,
      org_name
    );
    event_hubs.forEach(eh => {
      let anchorUpdateEventPromise = new Promise((resolve, reject) => {
        logger.debug("anchorUpdateEventPromise - setting up event");
        const event_timeout = setTimeout(() => {
          let message = "REQUEST_TIMEOUT:" + eh.getPeerAddr();
          logger.error(message);
          eh.disconnect();
        }, 60000);
        eh.registerBlockEvent(
          block => {
            logger.info(
              "The config update has been committed on peer %s",
              eh.getPeerAddr()
            );
            clearTimeout(event_timeout);
            resolve();
          },
          err => {
            clearTimeout(event_timeout);
            logger.error(err);
            reject(err);
          },
          // the default for 'unregister' is true for block listeners
          // so no real need to set here, however for 'disconnect'
          // the default is false as most event hubs are long running
          // in this use case we are using it only once
          { unregister: true, disconnect: true }
        );
        eh.connect();
      });
      promises.push(anchorUpdateEventPromise);
    });

    var sendPromise = client.updateChannel(request);
    // put the send to the orderer last so that the events get registered and
    // are ready for the orderering and committing
    promises.push(sendPromise);
    let results = await Promise.all(promises);
    logger.debug(util.format("------->>> R E S P O N S E : %j", results));
    let response = results.pop(); //  orderer results are last in the results

    if (response) {
      if (response.status === "SUCCESS") {
        logger.info(
          "Successfully update anchor peers to the channel %s",
          channelName
        );
      } else {
        error_message = util.format(
          "Failed to update anchor peers to the channel %s with status: %s reason: %s",
          channelName,
          response.status,
          response.info
        );
        logger.error(error_message);
      }
    } else {
      error_message = util.format(
        "Failed to update anchor peers to the channel %s",
        channelName
      );
      logger.error(error_message);
    }
  } catch (error) {
    logger.error(
      "Failed to update anchor peers due to error: " + error.stack
        ? error.stack
        : error
    );
    error_message = error.toString();
  }

  if (!error_message) {
    let message = util.format(
      "Successfully update anchor peers in organization %s to the channel '%s'",
      org_name,
      channelName
    );
    logger.info(message);
    const response = {
      success: true,
      message: message
    };
    return response;
  } else {
    let message = util.format(
      "Failed to update anchor peers. cause:%s",
      error_message
    );
    logger.error(message);
    const response = {
      success: false,
      message: message
    };
    return response;
  }
};

exports.updateAnchorPeers = updateAnchorPeers;
